/*  Copyright 2003  Auri Marcelo Rizzo Vicenzi, Marcio Eduardo Delamaro, 			    Jose Carlos Maldonado

    This file is part of Jabuti.

    Jabuti is free software: you can redistribute it and/or modify
    it under the terms of the GNU Lesser General Public License as 
    published by the Free Software Foundation, either version 3 of the      
    License, or (at your option) any later version.

    Jabuti is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU Lesser General Public License
    along with Jabuti.  If not, see <http://www.gnu.org/licenses/>.
*/


package br.jabuti.gvf;


/**
 * This is an example of a application that is written using the
 * Graph Visualization Framework API.
 *
 * @author  Plinio Vilela
 * @version 1.0 AUG 2002
 */

import java.util.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

import br.jabuti.gui.*;
import br.jabuti.project.*;
import br.jabuti.util.*;

import br.jabuti.graph.*;


public class CFGFrame extends JFrame implements ItemListener {

    // Variables to allow the relationship between 
    // this panel and the main window
    //private static JFrame mainWindow = null;    
    //ActiveProject activeProject = null;

    /**
	 * Added to jdk1.5.0_04 compiler
	 */
	private static final long serialVersionUID = -7718260415872121007L;
	// The current class file name and its methods
    String currentClassFile = null;
    HashMap methods = null;

    // The current set of nodes and links being showed
    Vector vetNodes;  // Vector of GVFNode
    Vector vetLinks;  // Vector of GVFLink

    // Variables declaration - do not modify//GEN-BEGIN:variables
    private JPanel topPanel;
    private static JPanel colorButtonPanel;
    private JComboBox methodCombo;
    
    JPanel cfgPanel;
    JScrollPane sp = null;
	
    JPanel buttonPanel;

    JPanel northPanel;
    static private JCheckBox showTip;
    static private JButton showButton;

	// Togle primary and secondary edges
	// from the CFGFrame.
	JPanel southPanel;
	static private JCheckBox showPrimary;
    static private JCheckBox showSecondary;
    static Color invisible;
    
    boolean changingClass=false;
	
    /** Creates new form draw */
    public CFGFrame() {
        super();
        
        showTip = new JCheckBox("Show Node Info");
        showTip.setSelected( true );

        showPrimary = new JCheckBox("Show Primary Edges");
        showPrimary.setSelected( true );
        showPrimary.addActionListener(new java.awt.event.ActionListener() {
         	public void actionPerformed(ActionEvent e) {
         		repaint();
         	}
         });

        showSecondary = new JCheckBox("Show Secondary Edges");
        showSecondary.setSelected( true );
        showSecondary.addActionListener(new java.awt.event.ActionListener() {
         	public void actionPerformed(ActionEvent e) {
         		repaint();
         	}
         });
    }

    /** This method is called from within the constructor to
     * initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is
     * always regenerated by the FormEditor.
     */
    private void initComponents() {// GEN-BEGIN:initComponents
        topPanel = new JPanel();
        topPanel.setLayout(new BorderLayout());
    	
        methodCombo = new JComboBox();
        
        methodCombo.addItemListener(this);

        updateColorButtonPanel();
				
        topPanel.add( methodCombo, BorderLayout.NORTH);
        topPanel.add( colorButtonPanel, BorderLayout.SOUTH);
                
        getContentPane().add(topPanel, BorderLayout.NORTH);

        GVFLayout ly = new GVFLayout(vetNodes, vetLinks);        
		
        cfgPanel = new GVFDrawPanel(ly.getMaxX() + 100, ly.getMaxY() + 200, vetNodes, vetLinks);
        cfgPanel.setVisible(true);

        sp = new JScrollPane(cfgPanel);

        sp.add(cfgPanel);
        sp.setViewportView(cfgPanel);
        getContentPane().add(sp, BorderLayout.CENTER);

        buttonPanel = new JPanel();
        buttonPanel.setLayout(new BorderLayout());

        northPanel = new JPanel();
        northPanel.setLayout(new FlowLayout());
        
        southPanel = new JPanel();
        southPanel.setLayout(new FlowLayout());
		
        showButton = new JButton("Show Decisions/Definitions");
        showButton.addActionListener(new java.awt.event.ActionListener() {
                    public void actionPerformed(ActionEvent e) {
                        if (JabutiGUI.isAllPrimaryEdges() || JabutiGUI.isAllSecondaryEdges() ) {
                            (JabutiGUI.mainWindow()).allPrimaryEdgesCriterion_actionPerformed(e);
                        } else if ( JabutiGUI.isAllPrimaryUses() || JabutiGUI.isAllSecondaryUses() ) {
                            (JabutiGUI.mainWindow()).allPrimaryUsesCriterion_actionPerformed(e);
                        }
                        repaint();
                        // showButton.setEnabled( false );
                    }
                }
                );
        
        northPanel.add(showTip);
        northPanel.add(showButton);
             
        southPanel.add( showPrimary );   
        southPanel.add( showSecondary );
        
        buttonPanel.add(northPanel, BorderLayout.NORTH );
        buttonPanel.add(southPanel, BorderLayout.SOUTH );
        buttonPanel.setBorder(BorderFactory.createEtchedBorder());

        getContentPane().add(buttonPanel, BorderLayout.SOUTH);
        
        setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE);    
        setSize(400, 600);   
		
        setIconImage(JabutiGUI.mainWindow().getIconImage());
    }// GEN-END:initComponents

    static public void updateColorButtonPanel() {
        if (colorButtonPanel == null)
            colorButtonPanel = new JPanel();
			
        // BUILDING THE COLOR PANEL...
        colorButtonPanel.setVisible(false);
        colorButtonPanel.removeAll();

        int[] labels = WeightColor.getColorBarLabels();
		        	     
        colorButtonPanel.setLayout(new GridLayout(1, labels.length));
	     	
        for (int i = 0; i < labels.length; i++) {
            Integer label = new Integer(labels[i]);
            JButton button = new JButton(label.toString());
	     
            int color = WeightColor.getColorValue( label );

            button.setBackground(ToolConstants.getColor(color));
	
            button.setBorderPainted(false);
            button.setFocusPainted(false);
            button.setEnabled(false);
	     
            colorButtonPanel.add(button);
        }			
        colorButtonPanel.setVisible(true);
    }

    public void itemStateChanged(ItemEvent e) {
    	if ( !changingClass ) { // to control concurrent access to methodCombo
	        String methodName = (String) methodCombo.getSelectedItem();
	        
	        if ( ! (JabutiGUI.getProject().getCurClassName().equals( currentClassFile ) ) ) {
		    	setSelectedClass( JabutiGUI.getProject().getCurClassName() );
		    	methodName = JabutiGUI.getProject().getCurMethodName();
		    }
		    	
		    buildCFG(methodName);	    	
		}
    }

    private void buildCFG(String methodName) {
        //System.out.println("Showing the CFG for: " + methodName);

        JabutiGUI.getProject().setCurMethodName( methodName );        
				
        ClassMethod cm = (ClassMethod) methods.get(methodName);

        vetNodes = new Vector();
        vetLinks = new Vector();

        CFG cfg = cm.getCFG();
        GraphNode[] fdt = cfg.findDFT(true);

        Hashtable nodesTable = new Hashtable();

        for (int i = 0; i < fdt.length; i++) {
            GraphNode gn = fdt[i];
            String label = gn.getLabel();
					
          //  System.out.println("Node added: " + label);

            GVFNode curNode;

            if (nodesTable.containsKey(label)) {
                curNode = (GVFNode) nodesTable.get(label);
            } else {
                if (gn instanceof CFGCallNode) {
                    curNode = new GVFCallNode(gn, cm);
                } else if (gn instanceof CFGExitNode
                        || ((gn.getPrimNext().size() == 0)
                        && (gn.getSecNext().size() == 0))) {
                    curNode = new GVFExitNode(gn, cm);
                } else {
                    curNode = new GVFNode(gn, cm);
                }
                        	
                nodesTable.put(label, curNode);
                vetNodes.addElement(curNode);
            }

			Vector v = null;
			
            // Creating the primary edges
            v = gn.getPrimNext();
	
            for (int j = 0; j < v.size(); j++) {
                GraphNode gn2 = ((GraphNode) v.elementAt(j));
                String childLabel = gn2.getLabel();
	
                //System.out.println("Edge added: <" + label + "," + childLabel + ">");
                GVFNode childNode;
	
                if (nodesTable.containsKey(childLabel)) {
                    childNode = (GVFNode) nodesTable.get(childLabel);
                } else {
                    if (gn2 instanceof CFGCallNode) {
                        childNode = new GVFCallNode(gn2, cm);
                    } else if (gn2 instanceof CFGExitNode
                            || ((gn2.getPrimNext().size() == 0)
                            && (gn2.getSecNext().size() == 0))) {
                        childNode = new GVFExitNode(gn2, cm);
                    } else {
                        childNode = new GVFNode(gn2, cm);
                    }
                        	
                    nodesTable.put(childLabel, childNode);
                    vetNodes.addElement(childNode);
                }
                
                GVFLink link = new GVFLink(curNode, childNode);

                // link.setLabel( "p" );
                vetLinks.addElement(link);
            }

            // Creating the secondary edges (exception edges)
            v = gn.getSecNext();
            for (int j = 0; j < v.size(); j++) {
                GraphNode gn2 = ((GraphNode) v.elementAt(j));
                String childLabel = gn2.getLabel();

                //System.out.println("Edge added: <" + label + "," + childLabel + ">");
                GVFNode childNode;
                        
                if (nodesTable.containsKey(childLabel)) {
                    childNode = (GVFNode) nodesTable.get(childLabel);
                } else {
                    if (gn2 instanceof CFGCallNode) {
                        childNode = new GVFCallNode(gn2, cm);
                    } else if (gn2 instanceof CFGExitNode
                            || ((gn2.getPrimNext().size() == 0)
                            && (gn2.getSecNext().size() == 0))) {
                        childNode = new GVFExitNode(gn2, cm);
                    } else {
                        childNode = new GVFNode(gn2, cm);
                    }
                        	
                    nodesTable.put(childLabel, childNode);
                    vetNodes.addElement(childNode);
                }

                GVFLink link = new GVFSecLink(curNode, childNode);

                // link.setLabel( "s" );
                vetLinks.addElement(link);
            }
        }

        GVFLayout ly = new GVFLayout(vetNodes, vetLinks);

        if (cfgPanel == null) {
            initComponents();
        } else {
            ((GVFDrawPanel) cfgPanel).setNodesLinks(vetNodes, vetLinks);
        }
 		
 		invisible = cfgPanel.getBackground();
 		       	
        ((GVFDrawPanel) cfgPanel).setPreferredSize(new Dimension(ly.getMaxX() + 100, ly.getMaxY() + 200));
        sp.setViewportView(cfgPanel);
    }

    public void show(String classFile) {
        if ((currentClassFile == null) || !(classFile.equals(currentClassFile))) {
            setSelectedClass(classFile); 
        }
        super.setVisible(true);
    }
	
    public static boolean showNodeTips() {
        return showTip.isSelected();
    }

    public static boolean showPrimary() {
        return showPrimary.isSelected();
    }

    public static boolean showSecondary() {
        return showSecondary.isSelected();
    }

	public static Color getBkColor() {
		return invisible;
	}

    public static void showButtonEnabled(boolean en) {
        showButton.setEnabled(en);
    }

    public void setSelectedMethod(String name) {
        if (methodCombo != null && methods != null) {
            if (methods.containsKey(name)) {
                methodCombo.setSelectedItem( name ); 
                buildCFG(name);
            }
        }
    }

    public void setSelectedClass(String classFile) {
        if (!(classFile.equals(currentClassFile))) {

			changingClass = true;
			
            currentClassFile = classFile;
		    
            this.setTitle("Def-Use Graph: " + classFile);
		    
            ClassFile cf = (ClassFile) (JabutiGUI.getProject()).getClassFile(currentClassFile);
	
            methods = cf.getMethodsTable();
				
            Object[] names = methods.keySet().toArray(new String[0]);
	
            Arrays.sort(names);

            // Every time shows when the class
            // file changes, shows the CFG of the first method
            buildCFG((String) names[0]);
			
            // Removing all previous method names
            if (methodCombo != null) {
                methodCombo.removeAllItems();
            }
			
            // Adding new methods
            for (int x = 0; x < names.length; x++) {
                String mName = (String) names[x];

                methodCombo.addItem(mName);
            }
            changingClass = false;
        } else {
            repaint();
        }
    }
	
    /**
     * @param args the command line arguments
     */
    public static void main(String args[]) {
    	System.out.println(System.getProperties());
    }
}

